package org.black.phoneUtility.sms.reply.app.service;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import java.util.List;

import org.black.phoneUtility.R;
import org.black.phoneUtility.database.sqlite.PhoneUtilitySQLiteOpenHelper;
import org.black.phoneUtility.sms.reply.SmsReplyConstant;
import org.black.phoneUtility.sms.reply.content.broadcastReceiver.SentSmsBroadcastReceiver;
import org.black.phoneUtility.sms.reply.database.dataModel.ReplyHistory;
import org.black.phoneUtility.sms.reply.provider.SmsReplyContract;
import org.black.phoneUtility.sms.reply.service.SmsReplyService;
import org.black.phoneUtility.sms.reply.util.DateTimeUtil;
import org.black.phoneUtility.util.AlarmManagerUtility;

import android.app.AlarmManager;
import android.app.IntentService;
import android.app.PendingIntent;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.Bundle;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.preference.PreferenceManager;
import android.provider.CallLog;
import android.provider.ContactsContract;
import android.provider.ContactsContract.PhoneLookup;
import android.telephony.PhoneNumberUtils;
import android.telephony.SmsManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;

public class SmsReplyReplyService extends IntentService {
	private static final String TAG = SmsReplyReplyService.class.getName();
	private static final String LOG_TAG = SmsReplyReplyService.class.getName();

	public SmsReplyReplyService() {
		super(SmsReplyReplyService.class.getName());
	}

	public static final String ARGUMENTS_NUMBERS = "arguments_incoming_numbers";
	public static final String ARGUMENTS_SMS_INBOX_IDS = "arguments_sms_inbox_ids";
	public static final String ARGUMENTS_CURRENT_DATE_TIME_IN_MILLISECONDS = "currentDateTimeInMilliseconds";
	public static final String ARGUMENTS_SMS_MESSAGE_BODIES = "smsMessageBody";
	public static final String ARGUMENTS_NUMBERS_TYPE = "numbersType";
	public static final String ARGUMENTS_ACTION_TYPE = "arguments_action_type";

	private static final String ARGUMENTS_SMS_REPLY_ID = "arguments_sms_reply_id";
	private static final String ARGUMENTS_MESSAGE = "arguments_message";
	private static final String ARGUMENTS_INCOMING_NUMBER = "arguments_incoming_number";
	private static final String ARGUMENTS_REPLY_NUMBERS = "arguments_reply_numbers";
	private static final String ARGUMENTS_SMS_INBOX_ID = "arguments_sms_inbox_id";
	private static final String ARGUMENTS_SMS_REPLY_MODIFICATION_DATE_TIME = "arguments_sms_reply_modification_date_time";

	public static final int ACTION_TYPE_NORMAL = 129;
	private static final int ACTION_TYPE_DELAY = 130;

	public static final int NUMBERS_TYPE_PHONE = ReplyHistory.SOURCE_FROM_TYPE_PHONE;
	public static final int NUMBERS_TYPE_SMS = ReplyHistory.SOURCE_FROM_TYPE_SMS;

	private static final long fiveMinutesAgeInMilliSeconds = System
			.currentTimeMillis() - 5l * 60 * 1000;

	private PhoneUtilitySQLiteOpenHelper phoneUtilitySQLiteOpenHelper = null;
	private SmsReplyService smsReplyService;
	private WakeLock wakeLock;

	private class WriteReplyHistoryRunnable implements Runnable {
		private Context context;
		private String message;
		private String destinationAddress;
		private long activationRecordId;
		private int numberType;

		public WriteReplyHistoryRunnable(Context context, String message,
				String destinationAddress, long activationRecordId,
				int numberType) {
			this.context = context;
			this.message = message;
			this.destinationAddress = destinationAddress;
			this.activationRecordId = activationRecordId;
			this.numberType = numberType;
		}

		@Override
		public void run() {
			SmsManager smsManager = SmsManager.getDefault();
			ArrayList<String> messages = smsManager.divideMessage(message);
			ArrayList<PendingIntent> sentIntents = new ArrayList<PendingIntent>();
			ArrayList<PendingIntent> deliveryIntents = new ArrayList<PendingIntent>();

			for (String dividedMessage : messages) {
				ContentValues replyHistoryContentValues = new ContentValues();
				replyHistoryContentValues
						.put(SmsReplyContract.ReplyHistories.REFERENCE_SMS_ACTIVATION_RECORDS_ID,
								activationRecordId);
				replyHistoryContentValues.put(
						SmsReplyContract.ReplyHistories.PHONE_NUMBER,
						destinationAddress);
				replyHistoryContentValues
						.put(SmsReplyContract.ReplyHistories.SOURCE_FROM,
								numberType);
				replyHistoryContentValues.put(
						SmsReplyContract.ReplyHistories.REPLY_MESSAGE,
						dividedMessage);
				replyHistoryContentValues.put(
						SmsReplyContract.ReplyHistories.CREATION_DATE_TIME,
						System.currentTimeMillis());
				replyHistoryContentValues.put(
						SmsReplyContract.ReplyHistories.REPLY_MESSAGE, message);
				Uri replyHistoryUri = context.getContentResolver().insert(
						SmsReplyContract.ReplyHistories.CONTENT_URI,
						replyHistoryContentValues);
				Intent sentIntent = new Intent(SmsReplyConstant.SMS_SENT_INTENT);
				sentIntent.setData(replyHistoryUri);
				sentIntent.putExtra(
						SentSmsBroadcastReceiver.ARGUMENTS_DESTINATION_ADDRESS,
						destinationAddress);
				sentIntent.putExtra(
						SentSmsBroadcastReceiver.ARGUMENTS_SMS_TEXT,
						dividedMessage);
				PendingIntent sentPendingIntent = PendingIntent.getBroadcast(
						context, 0, sentIntent,
						PendingIntent.FLAG_UPDATE_CURRENT);

				Intent deliverIntent = new Intent(
						SmsReplyConstant.SMS_DELIVERY_INTENT);
				deliverIntent.setData(replyHistoryUri);
				PendingIntent deliverPendingIntent = PendingIntent
						.getBroadcast(context, 0, deliverIntent,
								PendingIntent.FLAG_UPDATE_CURRENT);
				sentIntents.add(sentPendingIntent);
				deliveryIntents.add(deliverPendingIntent);
			}
			smsManager.sendMultipartTextMessage(destinationAddress, null,
					messages, sentIntents, deliveryIntents);
		}

	}

	@Override
	public void onCreate() {
		super.onCreate();
		this.phoneUtilitySQLiteOpenHelper = new PhoneUtilitySQLiteOpenHelper(
				this);
		this.smsReplyService = new SmsReplyService(this);
		PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
		this.wakeLock = powerManager.newWakeLock(
				PowerManager.PARTIAL_WAKE_LOCK, TAG);
		this.wakeLock.acquire();
	}

	@Override
	public void onDestroy() {
		super.onDestroy();
		this.phoneUtilitySQLiteOpenHelper.close();
		this.smsReplyService = null;
		this.phoneUtilitySQLiteOpenHelper = null;
		if (this.wakeLock != null && this.wakeLock.isHeld() == true) {
			this.wakeLock.release();
			this.wakeLock = null;
		}
	}

	@Override
	protected void onHandleIntent(Intent intent) {
		SharedPreferences sharedPreferences = PreferenceManager
				.getDefaultSharedPreferences(this);

		boolean doNotSendOnRoaming = sharedPreferences
				.getBoolean(
						getString(R.string.preference_sms_reply_do_not_send_reply_message_on_roaming),
						false);
		if (doNotSendOnRoaming == true) {
			TelephonyManager telephonyService = (TelephonyManager) this
					.getSystemService(Context.TELEPHONY_SERVICE);
			if (telephonyService != null
					&& telephonyService.isNetworkRoaming() == true) {
				return;
			}
		}

		Bundle bundle = intent.getExtras();
		if (bundle != null) {
			ArrayList<String> incomingNumbers = null;
			ArrayList<String> messageBodies = bundle
					.getStringArrayList(ARGUMENTS_SMS_MESSAGE_BODIES);
			ArrayList<String> smsInboxIds = bundle
					.getStringArrayList(ARGUMENTS_SMS_INBOX_IDS);
			int actionType = bundle.getInt(ARGUMENTS_ACTION_TYPE,
					ACTION_TYPE_NORMAL);
			long happendDateTimeInMilliseconds = bundle
					.getLong(ARGUMENTS_CURRENT_DATE_TIME_IN_MILLISECONDS);
			int numberType = bundle.getInt(ARGUMENTS_NUMBERS_TYPE);

			boolean enableAvoidExcessiveReply = sharedPreferences
					.getBoolean(
							getString(R.string.preference_sms_reply_enable_avoid_excessive_reply),
							true);

			if (numberType != NUMBERS_TYPE_PHONE
					&& numberType != NUMBERS_TYPE_SMS) {
				Log.e(LOG_TAG, "numberType: " + numberType
						+ " is not on support lists");
				return;
			}
			if (actionType != ACTION_TYPE_NORMAL
					&& actionType != ACTION_TYPE_DELAY) {
				Log.e(LOG_TAG, "actionType: " + actionType
						+ " is not on support lists");
				return;
			}
			boolean shouldReply = false;
			if (actionType == ACTION_TYPE_NORMAL
					|| actionType == ACTION_TYPE_DELAY) {
				if (actionType == ACTION_TYPE_DELAY) {
					long smsReplyId = bundle
							.getLong(ARGUMENTS_SMS_REPLY_ID, -1);
					String message = bundle.getString(ARGUMENTS_MESSAGE);
					Cursor smsReplyCursor = null;
					try {
						smsReplyCursor = this
								.getContentResolver()
								.query(ContentUris.withAppendedId(
										SmsReplyContract.SmsReplies.CONTENT_URI,
										smsReplyId),
										null,
										SmsReplyContract.SmsReplies.IS_ACTIVATED
												+ " = ?",
										new String[] { String.valueOf(1) },
										null);
						if (smsReplyCursor.moveToFirst() == true) {
							if (smsReplyCursor
									.getLong(smsReplyCursor
											.getColumnIndexOrThrow(SmsReplyContract.SmsReplies.MODIFICATION_DATE_TIME)) == bundle
									.getLong(
											ARGUMENTS_SMS_REPLY_MODIFICATION_DATE_TIME,
											-1)) {
								shouldReply = true;
							}
							if (shouldReply == true) {
								if (numberType == NUMBERS_TYPE_SMS) {
									Cursor smsInboxCursor = null;
									try {
										smsInboxCursor = this
												.getContentResolver()
												.query(Uri
														.parse("content://sms/inbox"),
														new String[] { "read" },
														"_id" + " = ? ",
														new String[] { bundle
																.getString(ARGUMENTS_SMS_INBOX_ID) },
														null);
										if (smsInboxCursor != null
												&& smsInboxCursor.moveToFirst() == true) {
											shouldReply = (1 == smsInboxCursor
													.getInt(smsInboxCursor
															.getColumnIndex("read"))) ? false
													: true;
										}
									} catch (Throwable throwable) {
										Log.e(LOG_TAG, "Unknown Error!",
												throwable);
									} finally {
										if (smsInboxCursor != null) {
											try {
												smsInboxCursor.close();
											} catch (Throwable th) {
												Log.e(LOG_TAG,
														"Fail to close cursor",
														th);
											}
										}
									}
								} else if (numberType == NUMBERS_TYPE_PHONE) {
									String incomingNumber = bundle
											.getString(ARGUMENTS_INCOMING_NUMBER);
									Cursor callLogCursor = null;
									try {
										callLogCursor = this
												.getContentResolver()
												.query(Uri
														.withAppendedPath(
																CallLog.Calls.CONTENT_FILTER_URI,
																Uri.encode(incomingNumber)),
														new String[] { CallLog.Calls.NUMBER },
														CallLog.Calls.TYPE
																+ " = "
																+ CallLog.Calls.OUTGOING_TYPE
																+ " AND "
																+ CallLog.Calls.DATE
																+ " >= ? ",
														new String[] { String
																.valueOf(happendDateTimeInMilliseconds) },
														CallLog.Calls.DATE
																+ " DESC");
										if (callLogCursor.moveToFirst() == true) {
											do {
												String number = callLogCursor
														.getString(callLogCursor
																.getColumnIndex(CallLog.Calls.NUMBER));
												if (number != null) {
													if (PhoneNumberUtils
															.compare(
																	this,
																	incomingNumber,
																	number) == true) {
														shouldReply = false;
														break;
													}
												}
											} while (callLogCursor.moveToNext() == true);
										}
									} catch (Throwable throwable) {
										Log.e(LOG_TAG, "Unknown Error!",
												throwable);
									} finally {
										if (callLogCursor != null) {
											try {
												callLogCursor.close();
											} catch (Throwable th) {
												Log.e(LOG_TAG,
														"Fail to close callLogCursor",
														th);
											}
										}

									}
								}
							}
							if (shouldReply == true) {
								String[] replyNumbers = bundle
										.getStringArray(ARGUMENTS_REPLY_NUMBERS);

								Long activationRecordId = this.smsReplyService
										.getActivationRecordId(smsReplyId);
								if (TextUtils.isEmpty(message) == true) {
									Log.e(LOG_TAG, "message is empty!");
									return;
								}
								if (activationRecordId == null) {
									Log.e(LOG_TAG,
											"activationRecordId is null!");
									return;
								}
								this.sendMessages(
										enableAvoidExcessiveReply,
										replyNumbers,
										smsReplyCursor.getInt(smsReplyCursor
												.getColumnIndexOrThrow(SmsReplyContract.SmsReplies.ENABLE_AVOID_EXCESSIVE_REPLY)) == 1,
										smsReplyId, activationRecordId,
										message, numberType);
							}
						}
					} catch (Throwable throwable) {
						Log.e(LOG_TAG, "Unknown Error!", throwable);
					} finally {
						if (smsReplyCursor != null) {
							try {
								smsReplyCursor.close();
							} catch (Throwable th) {
								Log.e(LOG_TAG, "Fail to close smsReplyCursor!",
										th);
							}
						}
					}
				} else {
					Cursor smsRepliesCursor = null;
					try {
						if (actionType == ACTION_TYPE_NORMAL) {
							smsRepliesCursor = this
									.getContentResolver()
									.query(SmsReplyContract.SmsReplies.CONTENT_URI,
											null,
											SmsReplyContract.SmsReplies.IS_ACTIVATED
													+ " = ?",
											new String[] { String.valueOf(1) },
											SmsReplyContract.SmsReplies.CREATEION_DATE_TIME
													+ " ASC");
						}

						if (smsRepliesCursor != null) {
							if (smsRepliesCursor.moveToFirst() == true) {
								if (numberType == NUMBERS_TYPE_PHONE) {
									incomingNumbers = bundle
											.getStringArrayList(ARGUMENTS_NUMBERS);
								} else if (numberType == NUMBERS_TYPE_SMS) {
									incomingNumbers = bundle
											.getStringArrayList(ARGUMENTS_NUMBERS);
								}
								do {
									long smsReplyId = smsRepliesCursor
											.getInt(smsRepliesCursor
													.getColumnIndex(SmsReplyContract.SmsReplies._ID));
									if (smsRepliesCursor
											.getInt(smsRepliesCursor
													.getColumnIndexOrThrow(SmsReplyContract.SmsReplies.SHOULD_RESPONSE_FROM_PHONE)) == 0
											&& numberType == NUMBERS_TYPE_PHONE) {
										Log.d(LOG_TAG,
												"Should not reply to Phone.");
										break;
									}
									if (smsRepliesCursor
											.getInt(smsRepliesCursor
													.getColumnIndexOrThrow(SmsReplyContract.SmsReplies.SHOULD_RESPONSE_FROM_SMS)) == 0
											&& numberType == NUMBERS_TYPE_SMS) {
										Log.d(LOG_TAG,
												"Should not reply to SMS.");
										break;
									}
									boolean inReplyDateTimePreiod = false;
									int replyDateTimeMode = smsRepliesCursor
											.getInt(smsRepliesCursor
													.getColumnIndex(SmsReplyContract.SmsReplies.REPLY_DATE_TIME_TYPE));
									switch (replyDateTimeMode) {
									case SmsReplyContract.SmsReplies.REPLY_DATE_TIME_MODE_ALWAYS_TYPE:
										inReplyDateTimePreiod = true;
										break;
									case SmsReplyContract.SmsReplies.REPLY_DATE_TIME_MODE_PERIOD_TYPE:
										if (smsRepliesCursor
												.isNull(smsRepliesCursor
														.getColumnIndex(SmsReplyContract.SmsReplies.PERIOD_START_DATE_TIME)) == false
												&& smsRepliesCursor
														.isNull(smsRepliesCursor
																.getColumnIndex(SmsReplyContract.SmsReplies.PERIOD_END_DATE_TIME)) == false) {
											if (happendDateTimeInMilliseconds >= smsRepliesCursor
													.getLong(smsRepliesCursor
															.getColumnIndex(SmsReplyContract.SmsReplies.PERIOD_START_DATE_TIME))
													&& happendDateTimeInMilliseconds < smsRepliesCursor
															.getLong(smsRepliesCursor
																	.getColumnIndex(SmsReplyContract.SmsReplies.PERIOD_END_DATE_TIME))) {
												inReplyDateTimePreiod = true;
											}
										}
										break;
									case SmsReplyContract.SmsReplies.REPLY_DATE_TIME_MODE_EXPIRED_AFTER_TYPE:
										if (smsRepliesCursor
												.isNull(smsRepliesCursor
														.getColumnIndex(SmsReplyContract.SmsReplies.EXPIRATED_AFTER_MILLISECONDS)) == false) {
											long expiredAfterMilliseconds = smsRepliesCursor
													.getLong(smsRepliesCursor
															.getColumnIndex(SmsReplyContract.SmsReplies.EXPIRATED_AFTER_MILLISECONDS));
											long activationDateTime = smsRepliesCursor
													.getLong(smsRepliesCursor
															.getColumnIndex(SmsReplyContract.SmsReplies.ACTIVATION_DATE_TIME));
											if (happendDateTimeInMilliseconds >= activationDateTime
													&& happendDateTimeInMilliseconds < activationDateTime
															+ expiredAfterMilliseconds) {
												inReplyDateTimePreiod = true;
											}
										}
										break;
									case SmsReplyContract.SmsReplies.REPLY_DATE_TIME_MODE_REPEAT_TYPE:
										Calendar currentCalendar = Calendar
												.getInstance();
										currentCalendar
												.setTimeInMillis(happendDateTimeInMilliseconds);

										int currentHour = currentCalendar
												.get(Calendar.HOUR_OF_DAY);
										int currentMinute = currentCalendar
												.get(Calendar.MINUTE);

										Calendar startCalendar = Calendar
												.getInstance();
										startCalendar
												.setTimeInMillis(smsRepliesCursor
														.getLong(smsRepliesCursor
																.getColumnIndex(SmsReplyContract.SmsReplies.REPEAT_START_TIME)));
										Calendar endCalendar = Calendar
												.getInstance();
										endCalendar
												.setTimeInMillis(smsRepliesCursor
														.getLong(smsRepliesCursor
																.getColumnIndex(SmsReplyContract.SmsReplies.REPEAT_END_TIME)));
										if (endCalendar.getTimeInMillis() <= startCalendar
												.getTimeInMillis()) {
											endCalendar.add(
													Calendar.DAY_OF_YEAR, 1);
										}

										int startHour = startCalendar
												.get(Calendar.HOUR_OF_DAY);
										int startMinute = startCalendar
												.get(Calendar.MINUTE);

										int endHour = endCalendar
												.get(Calendar.HOUR_OF_DAY);
										int endMinute = endCalendar
												.get(Calendar.MINUTE);

										if (startHour * 60 + startMinute >= endHour
												* 60 + endMinute) {
											endHour += 24;
										}
										int currentTimeInMinutes = currentHour
												* 60 + currentMinute;
										int startTimeInMinutes = startHour * 60
												+ startMinute;
										int endTimeInMinutes = endHour * 60
												+ endMinute;
										if (currentTimeInMinutes >= startTimeInMinutes
												&& currentTimeInMinutes < endTimeInMinutes) {
											inReplyDateTimePreiod = true;
										}

										if (DateTimeUtil
												.isNeverRepeat(smsRepliesCursor) == false
												&& inReplyDateTimePreiod == true) {
											int dayOfWeek = currentCalendar
													.get(Calendar.DAY_OF_WEEK);
											switch (dayOfWeek) {
											case Calendar.SUNDAY:
												if (smsRepliesCursor
														.getInt(smsRepliesCursor
																.getColumnIndexOrThrow(SmsReplyContract.SmsReplies.IS_SUNDAY_ENABLE)) == 0) {
													inReplyDateTimePreiod = false;
												}
												break;
											case Calendar.MONDAY:
												if (smsRepliesCursor
														.getInt(smsRepliesCursor
																.getColumnIndexOrThrow(SmsReplyContract.SmsReplies.IS_MONDAY_ENABLE)) == 0) {
													inReplyDateTimePreiod = false;
												}
												break;
											case Calendar.TUESDAY:
												if (smsRepliesCursor
														.getInt(smsRepliesCursor
																.getColumnIndexOrThrow(SmsReplyContract.SmsReplies.IS_TUESDAY_ENABLE)) == 0) {
													inReplyDateTimePreiod = false;
												}
												break;
											case Calendar.WEDNESDAY:
												if (smsRepliesCursor
														.getInt(smsRepliesCursor
																.getColumnIndexOrThrow(SmsReplyContract.SmsReplies.IS_WEDNESDAY_ENABLE)) == 0) {
													inReplyDateTimePreiod = false;
												}
												break;
											case Calendar.THURSDAY:
												if (smsRepliesCursor
														.getInt(smsRepliesCursor
																.getColumnIndexOrThrow(SmsReplyContract.SmsReplies.IS_THURSDAY_ENABLE)) == 0) {
													inReplyDateTimePreiod = false;
												}
												break;
											case Calendar.FRIDAY:
												if (smsRepliesCursor
														.getInt(smsRepliesCursor
																.getColumnIndexOrThrow(SmsReplyContract.SmsReplies.IS_FRIDAY_ENABLE)) == 0) {
													inReplyDateTimePreiod = false;
												}
												break;
											case Calendar.SATURDAY:
												if (smsRepliesCursor
														.getInt(smsRepliesCursor
																.getColumnIndexOrThrow(SmsReplyContract.SmsReplies.IS_SATURDAY_ENABLE)) == 0) {
													inReplyDateTimePreiod = false;
												}
												break;
											}
										}

										break;
									}
									if (incomingNumbers != null) {
										if (inReplyDateTimePreiod == true) {
											int incomingNumbersLength = incomingNumbers
													.size();
											for (int incomingNumbersIndex = 0; incomingNumbersIndex < incomingNumbersLength; incomingNumbersIndex++) {
												List<String> replyNumbers = new ArrayList<String>();
												shouldReply = false;
												String incomingNumber = incomingNumbers
														.get(incomingNumbersIndex);
												Cursor responseNumberFromCursor = null;
												try {
													responseNumberFromCursor = this
															.getContentResolver()
															.query(SmsReplyContract.ResponseNumberFrom.CONTENT_URI,
																	null,
																	SmsReplyContract.ResponseNumberFrom.REFERENCE_SMS_REPLY_ID
																			+ " = ?",
																	new String[] { String
																			.valueOf(smsReplyId) },
																	SmsReplyContract.ResponseNumberFrom.CREATION_DATE_TIME
																			+ " ASC");
													if (responseNumberFromCursor
															.moveToFirst() == true) {
														do {
															int type = responseNumberFromCursor
																	.getInt(responseNumberFromCursor
																			.getColumnIndex(SmsReplyContract.ResponseNumberFrom.TYPE));
															switch (type) {
															case SmsReplyContract.ResponseNumberFrom.ALL_NUMBERS_TYPE:
																shouldReply = true;
																break;
															case SmsReplyContract.ResponseNumberFrom.ALL_NUMBERS_IN_MY_CONTACTS_TYPE: {
																Cursor contactCursor = null;
																try {
																	contactCursor = this
																			.getContentResolver()
																			.query(Uri
																					.withAppendedPath(
																							PhoneLookup.CONTENT_FILTER_URI,
																							Uri.encode(incomingNumber)),
																					new String[] { ContactsContract.Contacts._ID },
																					null,
																					null,
																					null);
																	if (contactCursor
																			.moveToFirst() == true) {
																		shouldReply = true;
																		break;
																	} else {
																		Log.d(LOG_TAG,
																				"Not find Number:"
																						+ incomingNumber
																						+ " in contacts");
																	}
																} catch (Throwable throwable) {
																	Log.e(LOG_TAG,
																			"Query contact fileter uri fail!",
																			throwable);
																} finally {
																	if (contactCursor != null) {
																		try {
																			contactCursor
																					.close();
																		} catch (Throwable t) {
																			Log.e(LOG_TAG,
																					"Close contactCursor fail!",
																					t);
																		}
																	}
																}
																break;
															}
															case SmsReplyContract.ResponseNumberFrom.ALL_NUMBERS_NOT_IN_MY_CONTACTS_TYPE: {
																Cursor contactCursor = null;
																try {
																	contactCursor = this
																			.getContentResolver()
																			.query(Uri
																					.withAppendedPath(
																							PhoneLookup.CONTENT_FILTER_URI,
																							Uri.encode(incomingNumber)),
																					new String[] { ContactsContract.Contacts._ID },
																					null,
																					null,
																					null);
																	if (contactCursor
																			.moveToFirst() == false) {
																		shouldReply = true;
																		break;
																	} else {
																		Log.d(LOG_TAG,
																				"Find Number:"
																						+ incomingNumber
																						+ " in contacts");
																	}
																} catch (Throwable throwable) {
																	Log.e(LOG_TAG,
																			"Query contact fileter uri fail!",
																			throwable);
																} finally {
																	if (contactCursor != null) {
																		try {
																			contactCursor
																					.close();
																		} catch (Throwable t) {
																			Log.e(LOG_TAG,
																					"Close contactCursor fail!",
																					t);
																		}
																	}
																}
																break;
															}
															case SmsReplyContract.ResponseNumberFrom.CHOOSE_FROM_CONTACTS_TYPE:
															case SmsReplyContract.ResponseNumberFrom.USER_INPUT_TYPE:
															case SmsReplyContract.ResponseNumberFrom.NUMBERS_FROM_CONTACT_GROUP_TYPE: {
																if (PhoneNumberUtils
																		.compare(
																				this,
																				incomingNumber,
																				responseNumberFromCursor
																						.getString(responseNumberFromCursor
																								.getColumnIndexOrThrow(SmsReplyContract.ResponseNumberFrom.PHONE_NUMBER))) == true) {
																	shouldReply = true;
																	break;
																}
															}
															case SmsReplyContract.ResponseNumberFrom.MATCH_SMS_KEYWORD_TYPE: {
																if (numberType == NUMBERS_TYPE_SMS) {
																	String smsMessage = messageBodies
																			.get(incomingNumbersIndex);
																	String keyword = null;
																	if (responseNumberFromCursor
																			.isNull(responseNumberFromCursor
																					.getColumnIndexOrThrow(SmsReplyContract.ResponseNumberFrom.SMS_KEYWORD)) == false) {
																		keyword = responseNumberFromCursor
																				.getString(responseNumberFromCursor
																						.getColumnIndexOrThrow(SmsReplyContract.ResponseNumberFrom.SMS_KEYWORD));
																	}
																	if (TextUtils
																			.isEmpty(smsMessage) == false
																			&& TextUtils
																					.isEmpty(keyword) == false) {
																		if (smsMessage
																				.toLowerCase()
																				.indexOf(
																						keyword) > -1) {
																			shouldReply = true;
																		}
																	}
																}
															}
															default:
																break;
															}

															if (shouldReply == true) {
																break;
															}
														} while (responseNumberFromCursor
																.moveToNext() == true);
													}
												} finally {
													if (responseNumberFromCursor != null) {
														try {
															responseNumberFromCursor
																	.close();
														} catch (Throwable throwable) {
															Log.e(LOG_TAG,
																	"Fail to close responseNumberFromCursor",
																	throwable);
														}
													}
												}

												if (shouldReply == true) {
													int replyMode = smsRepliesCursor
															.getInt(smsRepliesCursor
																	.getColumnIndexOrThrow(SmsReplyContract.SmsReplies.REPLY_MODE));
													if (replyMode == SmsReplyContract.SmsReplies.REPLY_MODE_VALUE_REPLY_DIRECTLY) {
														replyNumbers
																.add(incomingNumber);
														shouldReply = true;
													} else if (replyMode == SmsReplyContract.SmsReplies.REPLY_MODE_VALUE_REPLY_TO_SPECIFIC_NUMBERS) {
														Cursor replyToSpecificNumberCursor = null;
														try {
															replyToSpecificNumberCursor = this
																	.getContentResolver()
																	.query(SmsReplyContract.ReplyToSpecificNumbers.CONTENT_URI,
																			null,
																			SmsReplyContract.ReplyToSpecificNumbers.REFERENCE_SMS_REPLY_ID
																					+ " = ? ",
																			new String[] { String
																					.valueOf(smsReplyId) },
																			SmsReplyContract.ResponseNumberFrom._ID
																					+ " ASC");
															if (replyToSpecificNumberCursor
																	.moveToFirst() == true) {
																do {
																	int type = replyToSpecificNumberCursor
																			.getInt(replyToSpecificNumberCursor
																					.getColumnIndex(SmsReplyContract.ReplyToSpecificNumbers.TYPE));
																	switch (type) {
																	case SmsReplyContract.ReplyToSpecificNumbers.FROM_CONTACT_TYPE:
																	case SmsReplyContract.ReplyToSpecificNumbers.USER_INPUT_TYPE:
																	case SmsReplyContract.ReplyToSpecificNumbers.NUMBERS_FROM_CONTACT_GROUP_TYPE: {
																		replyNumbers
																				.add(replyToSpecificNumberCursor
																						.getString(replyToSpecificNumberCursor
																								.getColumnIndex(SmsReplyContract.ReplyToSpecificNumbers.PHONE_NUMBER)));

																		break;
																	}
																	default:
																		break;
																	}
																} while (replyToSpecificNumberCursor
																		.moveToNext() == true);
																shouldReply = true;
															}
														} catch (Throwable throwable) {
															Log.e(LOG_TAG,
																	"Unknown Error!",
																	throwable);
														} finally {
															if (replyToSpecificNumberCursor != null) {
																try {
																	replyToSpecificNumberCursor
																			.close();
																} catch (Throwable th) {
																	Log.e(LOG_TAG,
																			"Fail to close replyToSpecificNumberCursor",
																			th);
																}
															}
														}
													} else {
														Log.e(LOG_TAG,
																"Reply Mode does not match any one. Serious error!");
														shouldReply = false;
													}
												}

												if (shouldReply == true) {
													Cursor noneReplyNumberCursor = null;
													try {
														noneReplyNumberCursor = this
																.getContentResolver()
																.query(SmsReplyContract.NoneReplyNumbers.CONTENT_URI,
																		null,
																		SmsReplyContract.NoneReplyNumbers.REFERENCE_SMS_REPLY_ID
																				+ " = ? ",
																		new String[] { String
																				.valueOf(smsReplyId) },
																		SmsReplyContract.NoneReplyNumbers._ID
																				+ " ASC");
														if (noneReplyNumberCursor
																.moveToFirst() == true) {
															do {
																Iterator<String> replyNumberIterator = replyNumbers
																		.iterator();
																while (replyNumberIterator
																		.hasNext() == true) {
																	String replyNumber = (String) replyNumberIterator
																			.next();
																	int type = noneReplyNumberCursor
																			.getInt(noneReplyNumberCursor
																					.getColumnIndex(SmsReplyContract.NoneReplyNumbers.TYPE));
																	switch (type) {
																	case SmsReplyContract.NoneReplyNumbers.ALL_NUMBERS_IN_MY_CONTACTS_TYPE:
																		Cursor contactCursor = null;
																		try {
																			contactCursor = this
																					.getContentResolver()
																					.query(Uri
																							.withAppendedPath(
																									PhoneLookup.CONTENT_FILTER_URI,
																									Uri.encode(replyNumber)),
																							new String[] { ContactsContract.Contacts._ID },
																							null,
																							null,
																							null);
																			if (contactCursor
																					.moveToFirst() == true) {
																				replyNumberIterator
																						.remove();
																				Log.d(LOG_TAG,
																						"Remove number: "
																								+ replyNumber
																								+ " because this number is in contacts.");
																			}
																		} catch (Throwable throwable) {
																			Log.e(LOG_TAG,
																					"Query contact fileter uri fail!",
																					throwable);
																		} finally {
																			if (contactCursor != null) {
																				try {
																					contactCursor
																							.close();
																				} catch (Throwable t) {
																					Log.e(LOG_TAG,
																							"Query contact fileter uri fail!",
																							t);
																				}
																			}
																		}
																		break;
																	case SmsReplyContract.NoneReplyNumbers.USER_INPUT_TYPE:
																	case SmsReplyContract.NoneReplyNumbers.CHOOSE_FROM_CONTACT_TYPE:
																	case SmsReplyContract.NoneReplyNumbers.NUMBERS_FROM_CONTACT_GROUP_TYPE: {
																		String phoneNumber = noneReplyNumberCursor
																				.getString(noneReplyNumberCursor
																						.getColumnIndexOrThrow(SmsReplyContract.NoneReplyNumbers.PHONE_NUMBER));
																		if (PhoneNumberUtils
																				.compare(
																						this,
																						replyNumber,
																						phoneNumber) == true) {
																			replyNumberIterator
																					.remove();
																			Log.d(LOG_TAG,
																					"Remove number: "
																							+ replyNumber
																							+ " because this number is match: "
																							+ phoneNumber);

																		}
																		break;
																	}
																	case SmsReplyContract.NoneReplyNumbers.MATCH_SMS_KEYWORD_TYPE:
																		if (numberType == NUMBERS_TYPE_SMS) {
																			String messageBody = messageBodies
																					.get(incomingNumbersIndex);
																			String keyword = noneReplyNumberCursor
																					.getString(noneReplyNumberCursor
																							.getColumnIndexOrThrow(SmsReplyContract.NoneReplyNumbers.SMS_KEYWORD));
																			if (messageBody
																					.toLowerCase()
																					.indexOf(
																							keyword) > -1) {
																				replyNumberIterator
																						.remove();
																				Log.d(LOG_TAG,
																						"Remove number: "
																								+ replyNumber
																								+ " because this message contains: "
																								+ keyword);
																			}
																		}
																		break;
																	default:
																		break;
																	}
																}
															} while (noneReplyNumberCursor
																	.moveToNext() == true);

														}
													} catch (Throwable throwable) {
														Log.e(LOG_TAG,
																"Unknown Error!",
																throwable);
													} finally {
														if (noneReplyNumberCursor != null) {
															try {
																noneReplyNumberCursor
																		.close();
															} catch (Throwable th) {
																Log.e(LOG_TAG,
																		"Fail to close noneReplyNumberCursor",
																		th);
															}
														}
													}

													if (shouldReply == true) {
														String message = null;
														if (smsRepliesCursor
																.getInt(smsRepliesCursor
																		.getColumnIndexOrThrow(SmsReplyContract.SmsReplies.PHONE_SMS_USE_DIFFERENT_MESSAGE)) == 0) {
															message = smsRepliesCursor
																	.getString(smsRepliesCursor
																			.getColumnIndexOrThrow(SmsReplyContract.SmsReplies.PHONE_MESSAGE));
														} else {
															if (numberType == NUMBERS_TYPE_PHONE) {
																message = smsRepliesCursor
																		.getString(smsRepliesCursor
																				.getColumnIndexOrThrow(SmsReplyContract.SmsReplies.PHONE_MESSAGE));
															} else if (numberType == NUMBERS_TYPE_SMS) {
																message = smsRepliesCursor
																		.getString(smsRepliesCursor
																				.getColumnIndexOrThrow(SmsReplyContract.SmsReplies.SMS_MESSAGE));
															} else {
																Log.e(LOG_TAG,
																		"Number type does not match.");
															}
														}
														if (TextUtils
																.isEmpty(message) == true) {
															Log.v(LOG_TAG,
																	"message is empty. do nothing!");
														} else {
															if (shouldReply == true) {
																if (numberType == NUMBERS_TYPE_PHONE) {
																	if (smsRepliesCursor
																			.getInt(smsRepliesCursor
																					.getColumnIndexOrThrow(SmsReplyContract.SmsReplies.ENABLE_NOT_RESPONSE_TO_RESPONSE_CALL)) == 1) {
																		if (smsRepliesCursor
																				.isNull(smsRepliesCursor
																						.getColumnIndex(SmsReplyContract.SmsReplies.DELAY_TIME_FOR_NOT_RESPONSE_TO_RESPONSE_CALL)) == false) {
																			long delayTime = smsRepliesCursor
																					.getLong(smsRepliesCursor
																							.getColumnIndex(SmsReplyContract.SmsReplies.DELAY_TIME_FOR_NOT_RESPONSE_TO_RESPONSE_CALL));
																			if (delayTime > 0l) {
																				long updateTimeInMillis = System
																						.currentTimeMillis()
																						+ delayTime;
																				AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
																				Intent delayIntent = new Intent();
																				delayIntent
																						.addCategory(String
																								.valueOf(smsReplyId)
																								+ updateTimeInMillis);
																				delayIntent
																						.setClass(
																								this,
																								SmsReplyReplyService.class);
																				delayIntent
																						.putExtra(
																								ARGUMENTS_MESSAGE,
																								message);
																				delayIntent
																						.putExtra(
																								ARGUMENTS_INCOMING_NUMBER,
																								incomingNumber);
																				delayIntent
																						.putExtra(
																								ARGUMENTS_REPLY_NUMBERS,
																								replyNumbers
																										.toArray(new String[replyNumbers
																												.size()]));
																				delayIntent
																						.putExtra(
																								ARGUMENTS_SMS_REPLY_MODIFICATION_DATE_TIME,
																								smsRepliesCursor
																										.getLong(smsRepliesCursor
																												.getColumnIndexOrThrow(SmsReplyContract.SmsReplies.MODIFICATION_DATE_TIME)));
																				delayIntent
																						.putExtra(
																								ARGUMENTS_NUMBERS_TYPE,
																								numberType);
																				delayIntent
																						.putExtra(
																								ARGUMENTS_ACTION_TYPE,
																								ACTION_TYPE_DELAY);
																				delayIntent
																						.putExtra(
																								ARGUMENTS_CURRENT_DATE_TIME_IN_MILLISECONDS,
																								happendDateTimeInMilliseconds);
																				delayIntent
																						.putExtra(
																								ARGUMENTS_SMS_REPLY_ID,
																								smsReplyId);
																				PendingIntent pendingIntent = PendingIntent
																						.getService(
																								this,
																								0,
																								delayIntent,
																								PendingIntent.FLAG_ONE_SHOT);
																				AlarmManagerUtility
																						.setAlarm(
																								alarmManager,
																								AlarmManager.RTC,
																								updateTimeInMillis,
																								pendingIntent);
																				shouldReply = false;
																			}
																		}
																	}

																}

																if (numberType == NUMBERS_TYPE_SMS) {
																	if (smsRepliesCursor
																			.getInt(smsRepliesCursor
																					.getColumnIndexOrThrow(SmsReplyContract.SmsReplies.ENABLE_NOT_RESPONSE_TO_READ_MESSAGE)) == 1) {
																		if (smsRepliesCursor
																				.isNull(smsRepliesCursor
																						.getColumnIndex(SmsReplyContract.SmsReplies.DELAY_TIME_FOR_NOT_RESPONSE_TO_READ_MESSAGE)) == false) {
																			long delayTime = smsRepliesCursor
																					.getLong(smsRepliesCursor
																							.getColumnIndex(SmsReplyContract.SmsReplies.DELAY_TIME_FOR_NOT_RESPONSE_TO_READ_MESSAGE));
																			if (delayTime > 0l) {
																				if (smsInboxIds != null
																						&& smsInboxIds
																								.size() > incomingNumbersIndex) {
																					long updateTimeInMillis = System
																							.currentTimeMillis()
																							+ delayTime;
																					String smsInboxId = smsInboxIds
																							.get(incomingNumbersIndex);
																					AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
																					Intent delayIntent = new Intent();
																					delayIntent
																							.addCategory(String
																									.valueOf(smsReplyId)
																									+ updateTimeInMillis);
																					delayIntent
																							.setClass(
																									this,
																									SmsReplyReplyService.class);
																					delayIntent
																							.putExtra(
																									ARGUMENTS_MESSAGE,
																									message);
																					delayIntent
																							.putExtra(
																									ARGUMENTS_REPLY_NUMBERS,
																									replyNumbers
																											.toArray(new String[replyNumbers
																													.size()]));
																					delayIntent
																							.putExtra(
																									ARGUMENTS_SMS_REPLY_MODIFICATION_DATE_TIME,
																									smsRepliesCursor
																											.getLong(smsRepliesCursor
																													.getColumnIndexOrThrow(SmsReplyContract.SmsReplies.MODIFICATION_DATE_TIME)));
																					delayIntent
																							.putExtra(
																									ARGUMENTS_NUMBERS_TYPE,
																									numberType);
																					delayIntent
																							.putExtra(
																									ARGUMENTS_ACTION_TYPE,
																									ACTION_TYPE_DELAY);
																					delayIntent
																							.putExtra(
																									ARGUMENTS_SMS_INBOX_ID,
																									smsInboxId);
																					delayIntent
																							.putExtra(
																									ARGUMENTS_SMS_REPLY_ID,
																									smsReplyId);
																					PendingIntent pendingIntent = PendingIntent
																							.getService(
																									this,
																									0,
																									delayIntent,
																									PendingIntent.FLAG_ONE_SHOT);
																					AlarmManagerUtility
																							.setAlarm(
																									alarmManager,
																									AlarmManager.RTC,
																									updateTimeInMillis,
																									pendingIntent);
																					shouldReply = false;
																				}
																			}
																		}
																	}

																}

															}
															if (shouldReply == true) {
																Long activationRecordId = this.smsReplyService
																		.getActivationRecordId(smsReplyId);
																if (activationRecordId != null) {
																	this.sendMessages(
																			enableAvoidExcessiveReply,
																			replyNumbers
																					.toArray(new String[replyNumbers
																							.size()]),
																			smsRepliesCursor
																					.getInt(smsRepliesCursor
																							.getColumnIndexOrThrow(SmsReplyContract.SmsReplies.ENABLE_AVOID_EXCESSIVE_REPLY)) == 1,
																			smsReplyId,
																			activationRecordId,
																			message,
																			numberType);
																}
															}

														}
													}
												}
											}
										} else {
											shouldReply = false;
											Log.d(LOG_TAG,
													"Not match in ReplyDateTimeMode");
										}
									}
								} while (smsRepliesCursor.moveToNext() == true);
							}
						}
					} catch (Throwable throwable) {
						Log.e(LOG_TAG, "Unknown Error!", throwable);
					} finally {
						if (smsRepliesCursor != null) {
							try {
								smsRepliesCursor.close();
							} catch (Throwable th) {
								Log.e(LOG_TAG,
										"Fail to close smsRepliesCursor", th);
							}
						}
					}
				}
			}
		}
	}

	private void sendSMSMessage(String message, String destinationAddress,
			long activationRecordId, int numberType) {
		new Thread(new WriteReplyHistoryRunnable(this, message,
				destinationAddress, activationRecordId, numberType)).start();
	}

	private boolean checkGlobalAvoidExcessiveReply(String destinationAddress) {
		boolean result = false;

		SQLiteDatabase sqLiteDatabase = null;
		Cursor cursor = null;
		try {
			sqLiteDatabase = phoneUtilitySQLiteOpenHelper.getReadableDatabase();
			String queryString = "SELECT "
					+ SmsReplyContract.ReplyHistories.PHONE_NUMBER
					+ " FROM "
					+ PhoneUtilitySQLiteOpenHelper.Tables.SMS.Reply.ACTIVATION_RECORDS
					+ " LEFT OUTER JOIN "
					+ PhoneUtilitySQLiteOpenHelper.Tables.SMS.Reply.REPLY_HISTORIES
					+ " ON "
					+ PhoneUtilitySQLiteOpenHelper.Tables.SMS.Reply.REPLY_HISTORIES
					+ "."
					+ SmsReplyContract.ReplyHistories.REFERENCE_SMS_ACTIVATION_RECORDS_ID
					+ " = "
					+ PhoneUtilitySQLiteOpenHelper.Tables.SMS.Reply.ACTIVATION_RECORDS
					+ "."
					+ SmsReplyContract.ActivationRecords._ID
					+ " WHERE "
					+ PhoneUtilitySQLiteOpenHelper.Tables.SMS.Reply.ACTIVATION_RECORDS
					+ "."
					+ SmsReplyContract.ActivationRecords.IS_ACTIVATED
					+ " = ? "
					+ " AND "
					+ PhoneUtilitySQLiteOpenHelper.Tables.SMS.Reply.REPLY_HISTORIES
					+ "."
					+ SmsReplyContract.ReplyHistories.CREATION_DATE_TIME
					+ " >= ? "
					+ " AND "
					+ PhoneUtilitySQLiteOpenHelper.Tables.SMS.Reply.REPLY_HISTORIES
					+ "."
					+ SmsReplyContract.ReplyHistories.REPLY_STATUS
					+ " = ? "
					+ " ORDER BY "
					+ PhoneUtilitySQLiteOpenHelper.Tables.SMS.Reply.REPLY_HISTORIES
					+ "." + SmsReplyContract.ReplyHistories.CREATION_DATE_TIME
					+ " DESC ";
			cursor = sqLiteDatabase
					.rawQuery(
							queryString,
							new String[] {
									String.valueOf(1),
									String.valueOf(fiveMinutesAgeInMilliSeconds),
									String.valueOf(SmsReplyContract.ReplyHistories.REPLY_STATUS_RESULT_OK) });
			if (cursor.moveToFirst() == true) {
				do {
					String phoneNumber = cursor
							.getString(cursor
									.getColumnIndex(SmsReplyContract.ReplyHistories.PHONE_NUMBER));
					if (PhoneNumberUtils.compare(this, destinationAddress,
							phoneNumber) == true) {
						Log.d(LOG_TAG, "destinationAddress: "
								+ destinationAddress + " and phoneNumber: "
								+ phoneNumber
								+ " are the same! Should not reply!");
						result = false;
						break;
					} else {
						result = true;
					}
				} while (cursor.moveToNext() == true);
			} else {
				result = true;
			}
		} catch (Throwable throwable) {
			Log.e(LOG_TAG, "Query reply history fail!", throwable);
		} finally {
			if (cursor != null) {
				try {
					cursor.close();
				} catch (Throwable th) {
					Log.e(LOG_TAG, "Close cursor fail!", th);
				}
			}
			if (sqLiteDatabase != null) {
				try {
					sqLiteDatabase.close();
				} catch (Throwable th) {
					Log.e(LOG_TAG, "Close sqLiteDatabase fail!", th);
				}
			}
		}

		return result;
	}

	private boolean checkAvoidExcessiveReply(long smsReplyId,
			String destinationAddress) {
		boolean result = false;

		SQLiteDatabase sqLiteDatabase = null;
		Cursor cursor = null;
		try {
			sqLiteDatabase = phoneUtilitySQLiteOpenHelper.getReadableDatabase();
			String queryString = "SELECT "
					+ SmsReplyContract.ReplyHistories.PHONE_NUMBER
					+ " FROM "
					+ PhoneUtilitySQLiteOpenHelper.Tables.SMS.Reply.ACTIVATION_RECORDS
					+ " LEFT OUTER JOIN "
					+ PhoneUtilitySQLiteOpenHelper.Tables.SMS.Reply.REPLY_HISTORIES
					+ " ON "
					+ PhoneUtilitySQLiteOpenHelper.Tables.SMS.Reply.REPLY_HISTORIES
					+ "."
					+ SmsReplyContract.ReplyHistories.REFERENCE_SMS_ACTIVATION_RECORDS_ID
					+ " = "
					+ PhoneUtilitySQLiteOpenHelper.Tables.SMS.Reply.ACTIVATION_RECORDS
					+ "."
					+ SmsReplyContract.ActivationRecords._ID
					+ " LEFT OUTER JOIN "
					+ PhoneUtilitySQLiteOpenHelper.Tables.SMS.Reply.REPLIES
					+ " ON "
					+ PhoneUtilitySQLiteOpenHelper.Tables.SMS.Reply.ACTIVATION_RECORDS
					+ "."
					+ SmsReplyContract.ActivationRecords.REFERENCE_SMS_REPLY_ID
					+ " = "
					+ PhoneUtilitySQLiteOpenHelper.Tables.SMS.Reply.REPLIES
					+ "."
					+ SmsReplyContract.SmsReplies._ID
					+ " WHERE "
					+ PhoneUtilitySQLiteOpenHelper.Tables.SMS.Reply.ACTIVATION_RECORDS
					+ "."
					+ SmsReplyContract.ActivationRecords.IS_ACTIVATED
					+ " = 1"
					+ " AND "
					+ PhoneUtilitySQLiteOpenHelper.Tables.SMS.Reply.REPLIES
					+ "."
					+ SmsReplyContract.SmsReplies._ID
					+ " = ?"
					+ " AND "
					+ PhoneUtilitySQLiteOpenHelper.Tables.SMS.Reply.REPLIES
					+ "."
					+ SmsReplyContract.SmsReplies.IS_ACTIVATED
					+ " = ? "
					+ " AND "
					+ PhoneUtilitySQLiteOpenHelper.Tables.SMS.Reply.REPLY_HISTORIES
					+ "."
					+ SmsReplyContract.ReplyHistories.CREATION_DATE_TIME
					+ " >= ? "
					+ " AND "
					+ PhoneUtilitySQLiteOpenHelper.Tables.SMS.Reply.REPLY_HISTORIES
					+ "."
					+ SmsReplyContract.ReplyHistories.REPLY_STATUS
					+ " = ? "
					+ " ORDER BY "
					+ PhoneUtilitySQLiteOpenHelper.Tables.SMS.Reply.REPLY_HISTORIES
					+ "." + SmsReplyContract.ReplyHistories.CREATION_DATE_TIME
					+ " DESC ";
			cursor = sqLiteDatabase
					.rawQuery(
							queryString,
							new String[] {
									String.valueOf(smsReplyId),
									String.valueOf(1),
									String.valueOf(fiveMinutesAgeInMilliSeconds),
									String.valueOf(SmsReplyContract.ReplyHistories.REPLY_STATUS_RESULT_OK) });
			if (cursor.moveToFirst() == true) {
				do {
					String phoneNumber = cursor
							.getString(cursor
									.getColumnIndex(SmsReplyContract.ReplyHistories.PHONE_NUMBER));
					if (PhoneNumberUtils.compare(this, destinationAddress,
							phoneNumber) == true) {
						Log.d(LOG_TAG, "destinationAddress: "
								+ destinationAddress + " and phoneNumber: "
								+ phoneNumber
								+ " are the same! Should not reply!");
						result = false;
						break;
					} else {
						result = true;
					}
				} while (cursor.moveToNext() == true);
			} else {
				result = true;
			}
		} catch (Throwable throwable) {
			Log.e(LOG_TAG, "Query reply history fail!", throwable);
		} finally {
			if (cursor != null) {
				try {
					cursor.close();
				} catch (Throwable th) {
					Log.e(LOG_TAG, "Close cursor fail!", th);
				}
			}
			if (sqLiteDatabase != null) {
				try {
					sqLiteDatabase.close();
				} catch (Throwable th) {
					Log.e(LOG_TAG, "Close sqLiteDatabase fail!", th);
				}
			}
		}

		return result;
	}

	private boolean checkReplyTimes(long smsReplyId, Long activationRecordId) {
		boolean result = true;
		Cursor replyTimesModeCursor = null;
		try {
			replyTimesModeCursor = this.getContentResolver().query(
					SmsReplyContract.ReplyTimesModes.CONTENT_URI,
					null,
					SmsReplyContract.ReplyTimesModes.REFERENCE_SMS_REPLY_ID
							+ " = ? " + " AND "
							+ SmsReplyContract.ReplyTimesModes.IS_ACTIVATED
							+ " = ? ",
					new String[] { String.valueOf(smsReplyId),
							String.valueOf(1) },
					SmsReplyContract.ReplyTimesModes._ID + " ASC");
			if (replyTimesModeCursor.moveToFirst() == true) {
				int type = replyTimesModeCursor.getInt(replyTimesModeCursor
						.getColumnIndex(SmsReplyContract.ReplyTimesModes.TYPE));

				if (activationRecordId != null) {
					switch (type) {
					case SmsReplyContract.ReplyTimesModes.ALWAYS_TYPE:
						break;
					case SmsReplyContract.ReplyTimesModes.LIMITED_TIMES_TYPE: {
						long limitTimes = replyTimesModeCursor
								.getLong(replyTimesModeCursor
										.getColumnIndexOrThrow(SmsReplyContract.ReplyTimesModes.TIMES));
						SQLiteDatabase sqLiteDatabase = null;
						Cursor replyHistoriesCursor = null;
						try {
							sqLiteDatabase = this.phoneUtilitySQLiteOpenHelper
									.getReadableDatabase();
							replyHistoriesCursor = sqLiteDatabase
									.rawQuery(
											"SELECT "
													+ "count("
													+ SmsReplyContract.ReplyHistories._ID
													+ ") "
													+ " FROM "
													+ PhoneUtilitySQLiteOpenHelper.Tables.SMS.Reply.REPLY_HISTORIES
													+ " WHERE "
													+ SmsReplyContract.ReplyHistories.REFERENCE_SMS_ACTIVATION_RECORDS_ID
													+ " = ? ",
											new String[] { activationRecordId
													.toString() });
							replyHistoriesCursor.moveToFirst();
							Long count = replyHistoriesCursor.getLong(0);

							if (count >= limitTimes) {
								result = false;
							}

						} catch (Throwable throwable) {
							Log.e(LOG_TAG, "Unknown Error!", throwable);
						} finally {
							if (replyHistoriesCursor != null) {
								try {
									replyHistoriesCursor.close();
								} catch (Throwable th) {
									Log.e(LOG_TAG,
											"Fail to replyHistoriesCursor", th);
								}
							}
							if (sqLiteDatabase != null) {
								try {
									sqLiteDatabase.close();
								} catch (Throwable th) {
									Log.e(LOG_TAG, "Fail to sqLiteDatabase", th);
								}
							}
						}
						break;
					}
					default:
						break;
					}
				} else {
					result = false;
				}
			}
		} catch (Throwable throwable) {
			Log.e(LOG_TAG, "Unknown Error!", throwable);
		} finally {
			if (replyTimesModeCursor != null) {
				try {
					replyTimesModeCursor.close();
				} catch (Throwable th) {
					Log.e(LOG_TAG, "Fail to close replyTiemsModeCursor", th);
				}
			}
		}
		return result;
	}

	private void sendMessages(boolean enableAvoidExcessiveReply,
			String[] replyNumbers, boolean smsReplyEnableAvoidExcessiveReply,
			long smsReplyId, long activationRecordId, String message,
			int numberType) {
		boolean shouldReply = false;
		for (String destinationNumber : replyNumbers) {
			if (SmsReplyConstant.UNKNOWN_NUMBER.equals(destinationNumber) == false) {
				if (enableAvoidExcessiveReply == true) {
					shouldReply = this
							.checkGlobalAvoidExcessiveReply(destinationNumber);
				} else {
					shouldReply = true;
				}
				if (shouldReply == true
						&& smsReplyEnableAvoidExcessiveReply == true) {
					shouldReply = this.checkAvoidExcessiveReply(smsReplyId,
							destinationNumber);
				}
				if (shouldReply == true) {
					shouldReply = this.checkReplyTimes(smsReplyId,
							activationRecordId);
				}
				if (shouldReply == true) {
					this.sendSMSMessage(message, destinationNumber,
							activationRecordId, numberType);
				}

			}
		}
	}
}
